﻿using System;
using System.Collections.Generic;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;
using Num = LFloatMath.Math.LFloat;

namespace IQIGame.Onigao.Logic
{
    public static partial class FormulaHelper
    {
        #region 主流程


        /// <summary>
        /// 计算
        /// </summary>
        /// <param name="rRePolish"></param>
        /// <param name="rResult"></param>
        /// <param name="rSelf"></param>
        /// <param name="rTarget"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static bool Calculate<T>(Formula rFormula, out T rResult, IFormulaObject rSelf = null, IFormulaObject rTarget = null)
        {
            var rRePolish = rFormula.rePolish;
            Stack<IFormulaOperand> rCalOpe = rFormula.tempStack;
            rCalOpe.Clear();
            for (int i = 0; i < rRePolish.Count; i++)
            {
                if (rRePolish[i] is IFormulaOperand operand)
                {
                    rCalOpe.Push(operand);
                }
                else
                {
                    FormulaOperator op = rRePolish[i] as FormulaOperator;
                    CalcOperatorBase(op, ref rCalOpe, rSelf, rTarget, rFormula);
                }
            }

            if (rCalOpe.Count == 1)
            {
                IFormulaOperand opd = rCalOpe.Pop();
                if (opd.TryGet(out rResult, rSelf, rTarget, rFormula))
                {
                    return true;
                }

                return false;
            }
            else
            {
                LogGame.LogError(string.Format("Get Battle Formual Value Error,final operand is not one"));
            }

            rResult = default;
            return false;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="rChunk"></param>
        /// <param name="rFormula"></param>
        /// <returns></returns>
        public static bool TryParse(string rChunk, Formula rFormula)
        {
            var rRePolish = rFormula.rePolish;
            var nIndex = 0;
            bool bAbleMinusSign = false; // 若出现‘-’是否是减号 
            Stack<FormulaOperator> rOperators = new Stack<FormulaOperator>(); //运算符堆栈
            while (FormulaHelper.TryPeek(rChunk, nIndex, out var rChar))
            {
                bool bAbleMinus = false; // 默认负号
                switch (rChar)
                {
                    case ' ':
                    case '\t':
                    case '\v':
                    case '\f':
                    case '\r':
                    case '\n':
                    case '\0':
                        nIndex++;
                        bAbleMinus = false;
                        break;
                    case ',':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.CA), ref rRePolish))
                        {
                            return false;
                        }

                        break;
                    case '(':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.LB), ref rRePolish))
                        {
                            return false;
                        }

                        break;
                    case ')':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.RB), ref rRePolish))
                        {
                            return false;
                        }

                        bAbleMinus = true;
                        break;
                    case '+':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.ADD), ref rRePolish))
                        {
                            return false;
                        }

                        break;
                    case '-':
                        nIndex++;
                        if (bAbleMinusSign)
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.SUB), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.NS), ref rRePolish))
                            {
                                return false;
                            }
                        }

                        break;
                    case '*':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.MUL), ref rRePolish))
                        {
                            return false;
                        }

                        break;
                    case '/':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.DIV), ref rRePolish))
                        {
                            return false;
                        }

                        break;
                    case '%':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.MOD), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    case '^':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.POW), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    case '<':
                        nIndex++;
                        if (FormulaHelper.TestPeek(rChunk, ref nIndex, '='))
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.LE), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.LT), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        break;
                    case '>':
                        nIndex++;
                        if (FormulaHelper.TestPeek(rChunk, ref nIndex, '='))
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.GE), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.GT), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        break;
                    case '!':
                        nIndex++;
                        if (FormulaHelper.TestPeek(rChunk, ref nIndex, '='))
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.UT), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.NOT), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        break;
                    case '=':
                        nIndex++;
                        if (FormulaHelper.TestPeek(rChunk, ref nIndex, '='))
                        {
                            if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.ET), ref rRePolish))
                            {
                                return false;
                            }
                        }
                        else
                        {
                            PrintCurPosParseError(0, nIndex, rChunk);
                            return false;
                        }
                        break;
                    case '&':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.AND), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    case '|':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.OR), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    case '?':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.QU), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    case ':':
                        nIndex++;
                        if (!AddAndDealOperator(rOperators, FormulaHelper.FetchOperator(FormulaOperator.OperatorType.CO), ref rRePolish))
                        {
                            return false;
                        }
                        break;
                    default:
                        // 数字 直接获取到完整数字后才会回到外层循环
                        if (rChar == '.' || char.IsDigit(rChar))
                        {
                            nIndex++;
                            var nNumStrCount = ParseNum(rChunk, nIndex, out var rOperand);
                            if (nNumStrCount > 0)
                            {
                                rRePolish.Add(rOperand);
                                bAbleMinus = true;
                                nIndex += nNumStrCount - 1; // 第一位也是数字，所以需要减一
                            }
                            else
                            {
                                PrintCurPosParseError(0, nIndex, rChunk);
                                return false;
                            }
                        }
                        else
                        {
                            var nIdentNum = ParseIdentifier(rChunk, nIndex, out var op, out var bIsOperator, out var nAttrSign);
                            if (nIdentNum > 0)
                            {
                                if (bIsOperator)
                                {
                                    AddAndDealOperator(rOperators, op as FormulaOperator, ref rRePolish);
                                }
                                else
                                {
                                    rRePolish.Add(op);
                                    bAbleMinus = true;
                                    // 属性值需要在计算时候把Unit塞进去，在这里缓存一下 
                                    if (nAttrSign == 1)
                                    {
                                        rFormula.selfAttr.Add(op as FormulaOperandAttr);
                                    }
                                    else if (nAttrSign == 2)
                                    {
                                        rFormula.targetAttr.Add(op as FormulaOperandAttr);
                                    }
                                }

                                nIndex += nIdentNum;
                            }
                            else
                            {
                                PrintCurPosParseError(0, nIndex, rChunk);
                                return false;
                            }
                        }
                        break;
                }

                bAbleMinusSign = bAbleMinus;
            }

            //转换完成,若运算符堆栈中尚有运算符时,
            //则依序取出运算符到逆波兰式中,直到运算符堆栈为空
            while (rOperators.Count > 0)
            {
                rRePolish.Add(rOperators.Pop());
            }
            return true;
        }

        //运算公式中的操作符,处理基础运算
        private static void CalcOperatorBase(FormulaOperator op, ref Stack<IFormulaOperand> rCalOpe, IFormulaObject rSelf, IFormulaObject rTarget, Formula rFormula)
        {
            switch (op.Type)
            {
                case FormulaOperator.OperatorType.NS:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                        result.Set(-op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("-ns opertor need one number");
                    }
                    opd1.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.ADD:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                        result.Set(op1 + op2);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("+ opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.SUB:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                        result.Set(op2 - op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("- opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.MUL:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                        result.Set(op1 * op2);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("* opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.MOD:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        if (op1 == 0)
                        {
                            throw new Exception("% opertor opd1 can't be zero");
                        }
                        else
                        {
                            IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                            result.Set(op2 % op1);
                            rCalOpe.Push(result);
                        }
                    }
                    else
                    {
                        throw new Exception("% opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.DIV:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        if (op1 == 0)
                        {
                            throw new Exception("/ opertor opd1 can't be zero");
                        }
                        else
                        {
                            IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                            result.Set(op2 / op1);
                            rCalOpe.Push(result);
                        }
                    }
                    else
                    {
                        throw new Exception("/ opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.POW:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        if (op1._val % Num.Precision != 0)
                        {
                            throw new Exception("Pow 运算幂参数必须为整数");
                        }
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Num);
                        result.Set(LMath.Pow(op2, op1));
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("Pow opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.GE:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 >= op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception(">= opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.GT:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 > op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception(">= opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.LE:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 <= op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("<= opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.LT:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 < op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("< opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.UT:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 != op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("!= opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.ET:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<Num>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<Num>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op2 == op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("== opertor need two number");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.NOT:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    if (opd1.TryGet<bool>(out var op1, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(!op1);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("! opertor need one boolvalue");
                    }
                    opd1.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.AND:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<bool>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<bool>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op1 && op2);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("& opertor need two boolvalue");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.OR:
                {
                    IFormulaOperand opd1 = rCalOpe.Pop();
                    IFormulaOperand opd2 = rCalOpe.Pop();
                    if (opd1.TryGet<bool>(out var op1, rSelf, rTarget, rFormula) && opd2.TryGet<bool>(out var op2, rSelf, rTarget, rFormula))
                    {
                        IFormulaOperand result = FormulaHelper.FetchOperand(EFormulaOperandType.Bool);
                        result.Set(op1 || op2);
                        rCalOpe.Push(result);
                    }
                    else
                    {
                        throw new Exception("| opertor need two boolvalue");
                    }
                    opd1.TryFree();
                    opd2.TryFree();
                    break;
                }
                case FormulaOperator.OperatorType.FUNC:
                {
                    //处理自定义函数
                    var rFunc = FormulaHelper.GetCustomFunction((int)op.FuncType);
                    rFunc.Calculate(rCalOpe, rSelf, rTarget, rFormula);
                    break;
                }
            }
        }

        /// <summary>
        /// 解析中添加运算符
        /// </summary>
        /// <param name="operators"></param>
        /// <param name="op"></param>
        /// <param name="rRePolish"></param>
        /// <returns></returns>
        private static bool AddAndDealOperator(Stack<FormulaOperator> operators, FormulaOperator op, ref List<object> rRePolish)
        {
            switch (op.Type)
            {
                //若当前运算符为左括号,则直接存入堆栈。
                case FormulaOperator.OperatorType.LB:
                    operators.Push(op);
                    return true;

                //若当前运算符为右括号,则依次弹出运算符堆栈中的运算符并存入到逆波兰式,直到遇到左括号为止,此时抛弃该左括号.
                case FormulaOperator.OperatorType.RB:
                    while (operators.Count > 0)
                    {
                        FormulaOperator curOp = operators.Pop();
                        if (curOp.Type != FormulaOperator.OperatorType.LB)
                        {
                            // if (curOp.Type == FormulaOperator.OperatorType.TERNARY) // 暂时不管三目
                            //     _teCount = _teCount > 0 ? _teCount - 1 : 0;
                            rRePolish.Add(curOp);
                        }
                        else
                        {
                            break;
                        }
                    }

                    return true;
                case FormulaOperator.OperatorType.CA:
                    while (operators.Count > 0)
                    {
                        FormulaOperator curOp = operators.Peek();
                        if (curOp.Type != FormulaOperator.OperatorType.LB)
                        {
                            operators.Pop();
                            rRePolish.Add(curOp);
                        }
                        else
                        {
                            break;
                        }
                    }

                    return true;
                default:
                    //若运算符堆栈为空,或者若运算符堆栈栈顶为左括号,则将当前运算符直接存入运算符堆栈.
                    if (operators.Count == 0 || operators.Peek().Type == FormulaOperator.OperatorType.LB)
                    {
                        operators.Push(op);
                        return true;
                    }

                    //若当前运算符优先级大于运算符栈顶的运算符,则将当前运算符直接存入运算符堆栈.
                    if (FormulaOperator.ComparePriority(op.Type, operators.Peek().Type) > 0)
                    {
                        operators.Push(op);
                    }
                    else
                    {
                        //若当前运算符若比运算符堆栈栈顶的运算符优先级低或相等，则输出栈顶运算符到逆波兰式，直至运算符栈栈顶运算符低于（不包括等于）该运算符优先级，
                        //或运算符栈栈顶运算符为左括号
                        //并将当前运算符压入运算符堆栈。
                        while (operators.Count > 0)
                        {
                            FormulaOperator peek = operators.Peek();
                            if (FormulaOperator.ComparePriority(op.Type, peek.Type) <= 0 && peek.Type != FormulaOperator.OperatorType.LB)
                            {
                                rRePolish.Add(operators.Pop());

                                if (operators.Count == 0)
                                {
                                    operators.Push(op);
                                    break;
                                }
                            }
                            else
                            {
                                operators.Push(op);
                                break;
                            }
                        }
                    }

                    return true;
            }
        }

        /// <summary>
        /// 解析数字
        /// </summary>
        /// <param name="rChunk"></param>
        /// <param name="nStartIndex"></param>
        /// <param name="rOperand"></param>
        /// <returns></returns>
        private static int ParseNum(string rChunk, int nStartIndex, out IFormulaOperand rOperand)
        {
            decimal number = 0;
            int nStartPos = nStartIndex - 1;
            var nIndex = nStartIndex;
            int nCount = 1;
            while (FormulaHelper.TryPeek(rChunk, nIndex, out var c))
            {
                if (c == '.' || char.IsDigit(c))
                {
                    nCount++;
                    nIndex++;
                }
                else
                {
                    break;
                }
            }

            if (!decimal.TryParse(rChunk.Substring(nStartPos, nCount), out number))
            {
                rOperand = null;
                return 0;
            }

            rOperand = FormulaHelper.FetchOperand(EFormulaOperandType.Num, true);
            rOperand.Set(new LFloat(true, (long)(number * LFloat.Precision)));
            return nCount;
        }

        /// <summary>
        /// 解析既不是数字也不是运算符的部分，如函数，true，false，特殊对象
        /// </summary>
        /// <param name="rChunk"></param>
        /// <param name="nStartIndex"></param>
        /// <param name="op"></param>
        /// <param name="bIsOperator"></param>
        /// <param name="nAttrSign"></param>
        /// <returns></returns>
        private static int ParseIdentifier(string rChunk, int nStartIndex, out object op, out bool bIsOperator, out int nAttrSign)
        {
            int nCount = 0;
            int nStartPos = nStartIndex;
            var nIndex = nStartIndex;
            op = null;
            bIsOperator = false;
            nAttrSign = 0;
            while (FormulaHelper.TryPeek(rChunk, nIndex, out var c))
            {
                if (c == '@' || c == '#' || c == '$' || c == '_' || char.IsLetter(c))
                {
                    nCount++;
                    nIndex++;
                }
                else if (char.IsDigit(c) && nCount > 0)
                {
                    nCount++;
                    nIndex++;
                }
                else
                {
                    break;
                }
            }

            if (nCount <= 0)
            {
                return nCount;
            }

            var rIdentStr = rChunk.Substring(nStartPos, nCount);

            if (string.IsNullOrEmpty(rIdentStr))
            {
                return 0;
            }

            if (rIdentStr == "true")
            {
                IFormulaOperand operand = FormulaHelper.FetchOperand(EFormulaOperandType.Bool, true);
                operand.Set(true);
                op = operand;
            }
            else if (rIdentStr == "false")
            {
                IFormulaOperand operand = FormulaHelper.FetchOperand(EFormulaOperandType.Bool, true);
                operand.Set(false);
                op = operand;
            }
            else if (rIdentStr[0] == '@' || rIdentStr[0] == '#')
            {
                IFormulaOperand operand = FormulaHelper.FetchOperand(EFormulaOperandType.Attr, true);
                if (rIdentStr[0] == '@')
                {
                    nAttrSign = 1; // 自己
                }
                else
                {
                    nAttrSign = 2;
                }

                rIdentStr = rIdentStr.Substring(1);
                operand.SetParam<Num>(rIdentStr);
                op = operand;
            }
            else if (rIdentStr[0] == '$')
            {
                rIdentStr = rIdentStr.Substring(1);
                if (int.TryParse(rIdentStr, out var nID))
                {
                    if (nID >= 0)
                    {
                        FormulaOperandDynamic rOperand = FormulaHelper.FetchOperand(EFormulaOperandType.DynamicIndex, true) as FormulaOperandDynamic;
                        rOperand.Set(nID);
                        op = rOperand;
                    }
                    // TODO 嵌套
                    // IFormulaOperand operand = FormulaHelper.FetchOperand(index);
                    // ident = operand;
                    // return true;
                }
            }
            else
            {
                bIsOperator = true;

                if (FormulaHelper.TryParseFunc(rIdentStr, out var rOperator))
                {
                    op = rOperator;
                }
                else
                {
                    PrintCurPosParseError(0, nStartPos, rChunk, $"未找到函数{rIdentStr}");
                    nCount = 0;
                }
            }

            return nCount;
        }


        #endregion

        public static bool TryPeek(string rChunk, int nIndex, out char rChar)
        {
            if (nIndex >= rChunk.Length)
            {
                rChar = ' ';
                return false;
            }

            rChar = rChunk[nIndex];
            return true;
        }

        public static bool TestPeek(string rChunk, ref int nIndex, char rTargetChar)
        {
            if (nIndex < rChunk.Length)
            {
                if (rChunk[nIndex] == rTargetChar)
                {
                    nIndex++;
                    return true;
                }
            }

            return false;
        }

        static FormulaHelper()
        {
            InitializeFunction();
        }

        #region FormulaOperand 操作

        private static Dictionary<EFormulaOperandType, Stack<IFormulaOperand>> m_OperandPool = new Dictionary<EFormulaOperandType, Stack<IFormulaOperand>>();

        public static void Set<T>(this IFormulaOperand rSelf, T value)
        {
            if (rSelf is null) return;
            TFormulaOperand<T> t = rSelf as TFormulaOperand<T>;
            t.SetValue(value);
        }

        public static void SetParam<T>(this IFormulaOperand rSelf, object rParam1 = null, object rParam2 = null, object rParam3 = null)
        {
            if (rSelf is null) return;
            TFormulaOperand<T> t = rSelf as TFormulaOperand<T>;
            t.SetParam(rParam1, rParam2, rParam3);
        }

        public static bool TryGet<T>(this IFormulaOperand rOpd, out T rResult, IFormulaObject rSelf, IFormulaObject rTarget, Formula rFormula)
        {
            if (rOpd is TFormulaOperand<T> t)
            {
                rResult = t.GetValue(rSelf, rTarget);
                return true;
            }
            else if (rFormula != null && rOpd is FormulaOperandDynamic rDynamic)
            {
                var nIndex = rDynamic.GetValue(rSelf, rTarget);
                return rFormula.TryGetDynamic(nIndex, out rResult, rSelf, rTarget);
            }

            rResult = default;
            return false;
        }

        public static IFormulaOperand FetchOperand(EFormulaOperandType rType, bool bByFormula = false)
        {
            if (m_OperandPool.TryGetValue(rType, out var rStack) && rStack.TryPop(out var rObj))
            {
                rObj.parseByFormula = bByFormula;
                return rObj;
            }

            switch (rType)
            {
                case EFormulaOperandType.Bool:
                    rObj = new FormulaOperandBool();
                    break;
                case EFormulaOperandType.Num:
                    rObj = new FormulaOperandNum();
                    break;
                case EFormulaOperandType.Attr:
                    rObj = new FormulaOperandAttr();
                    break;
                case EFormulaOperandType.DynamicIndex:
                    rObj = new FormulaOperandDynamic();
                    break;
                // case EFormulaOperandType.String:
                //     rObj = new FormulaOperandStr();
                //     break;
                // case EFormulaOperandType.Object :
                //     rObj = new FormulaOperandObj();
                //     break;
                default:
                    throw new ArgumentException("使用了未定义的计算参数");
            }

            rObj.parseByFormula = bByFormula;
            return rObj;
        }

        public static void TryFree(this IFormulaOperand rSelf, bool bForce = false)
        {
            if (true && (!rSelf.parseByFormula || bForce))
            {
                var rType = rSelf.opType;
                if (!FormulaHelper.m_OperandPool.TryGetValue(rType, out var rStack))
                {
                    m_OperandPool[rType] = new Stack<IFormulaOperand>();
                    rStack = m_OperandPool[rType];
                }

                rSelf.Reset();
                rStack.Push(rSelf);
            }
        }

        public static void ClearPool()
        {
            m_OperandPool.Clear();
            m_Operators.Clear();
        }

        #endregion

        #region FormulaOperator 操作

        private static Stack<FormulaOperator> m_Operators = new Stack<FormulaOperator>();

        public static FormulaOperator FetchOperator(FormulaOperator.OperatorType rType)
        {
            if (!m_Operators.TryPop(out var rOp))
            {
                rOp = new FormulaOperator();
            }

            return rOp.Initialize(rType);
        }

        public static FormulaOperator FetchOperator(FormulaOperator.OperatorType rType, int nFuncType)
        {
            if (!m_Operators.TryPop(out var rOp))
            {
                rOp = new FormulaOperator();
            }

            return rOp.Initialize(rType, nFuncType);
        }

        public static void Free(this FormulaOperator self)
        {
            self.Reset();
            FormulaHelper.m_Operators.Push(self);
        }

        #endregion

        #region BattleFunc 操作

        public static FormulaFunction[] m_FunctionCache;

        public static IformulaFunctionParse functionParser;

        public interface IformulaFunctionParse
        {
            bool TryParseFunc(string rIdentStr, out FormulaOperator op);
        }

        public static bool TryParseFunc(string rIdentStr, out FormulaOperator op)
        {
            if (System.Enum.TryParse<EFormulaFunctionType>(rIdentStr, true, out var rType))
            {
                op = FetchOperator(FormulaOperator.OperatorType.FUNC, (int)rType);
                return true;
            }

            op = null;
            return functionParser?.TryParseFunc(rIdentStr, out op) ?? false;
        }

        public static FormulaFunction GetCustomFunction(EFormulaFunctionType rType)
        {
            if (rType < EFormulaFunctionType.MAXCOUNT)
            {
                return m_FunctionCache[(int)rType];
            }

            throw new ArgumentException("未获取到对应类型的FormulaFunc");
        }

        public static FormulaFunction GetCustomFunction(int nIndex)
        {
            if (nIndex < m_FunctionCache.Length)
            {
                return m_FunctionCache[nIndex];
            }

            throw new ArgumentException("未获取到对应类型的FormulaFunc");
        }

        public static void InitializeFunction(int nCount = 0)
        {
            if (nCount == 0)
            {
                nCount = (int)EFormulaFunctionType.MAXCOUNT;
            }

            m_FunctionCache = new FormulaFunction[nCount];

            m_FunctionCache[(int)EFormulaFunctionType.Min] = new FormulaFunction_Min();
            m_FunctionCache[(int)EFormulaFunctionType.Max] = new FormulaFunction_Max();
            m_FunctionCache[(int)EFormulaFunctionType.Abs] = new FormulaFunction_Abs();
            m_FunctionCache[(int)EFormulaFunctionType.Sqrt] = new FormulaFunction_Sqrt();
            m_FunctionCache[(int)EFormulaFunctionType.Floor] = new FormulaFunction_Floor();
            m_FunctionCache[(int)EFormulaFunctionType.Ceil] = new FormulaFunction_Ceil();
            m_FunctionCache[(int)EFormulaFunctionType.Round] = new FormulaFunction_Round();
            m_FunctionCache[(int)EFormulaFunctionType.If] = new FormulaFunction_If();
            m_FunctionCache[(int)EFormulaFunctionType.FNum] = new FormulaFunction_FNum();
            m_FunctionCache[(int)EFormulaFunctionType.FBool] = new FormulaFunction_FBool();

            m_FunctionCache[(int)EFormulaFunctionType.bRand] = new FormulaFunction_BattleRand();
            m_FunctionCache[(int)EFormulaFunctionType.bBuffStack] = new FormulaFunction_BuffStack();
            m_FunctionCache[(int)EFormulaFunctionType.bCustomInt] = new FormulaFunction_CustomInt();
            m_FunctionCache[(int)EFormulaFunctionType.bSkill] = new FormulaFunction_Skill();
            m_FunctionCache[(int)EFormulaFunctionType.bTeam] = new FormulaFunction_Team();
            m_FunctionCache[(int)EFormulaFunctionType.bMaxAttribute] = new FormulaFunction_MaxAttribute();
            m_FunctionCache[(int)EFormulaFunctionType.bCustomProperty] = new FormulaFunction_CustomProperty();
            m_FunctionCache[(int)EFormulaFunctionType.bBackstab] = new FormulaFunction_Backstab();
            m_FunctionCache[(int)EFormulaFunctionType.bDistance] = new FormulaFunction_Distance();
        }

        #endregion

        //打印解析错误信息
        private static void PrintCurPosParseError(int nFormulaID, int nIndex, string rChunk, object rMessage = null)
        {
            LogGame.LogError(string.Format("Parse Battle Formual({0}) error,col:{1}, value:{2}, {3}", nFormulaID.ToString(), nIndex.ToString(), rChunk, rMessage?.ToString()));
        }
    }
}